package com.google.code.gaeom.impl;

import java.util.Map;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.repackaged.com.google.common.collect.Maps;

/**
 * @author Peter Murray <gaeom@pmurray.com>
 */
public class InstanceCache
{
	final InstanceCache upstream;
	final Map<Key, Object> keyToObject = Maps.newHashMap();
	final Map<Identity<Object>, Key> objectToKey = Maps.newHashMap();

	private static class Identity<T>
	{
		final T object;

		Identity(T object)
		{
			this.object = object;
		}

		@Override
		public int hashCode()
		{
			return System.identityHashCode(object);
		}

		@Override
		public boolean equals(Object obj)
		{
			return object == ((Identity<?>) obj).object;
		}

	}

	public InstanceCache()
	{
		this(null);
	}

	public InstanceCache(InstanceCache upstream)
	{
		this.upstream = upstream;
	}

	public Key getKey(Object object)
	{
		Key key = objectToKey.get(new Identity<Object>(object));
		if (key == null && upstream != null)
		{
			key = upstream.getKey(object);
			if (key != null)
				set(object, key);
		}
		return key;
	}

	@SuppressWarnings("unchecked")
	public <T> T getObject(Key key)
	{
		Object obj = keyToObject.get(key);
		if (obj == null && upstream != null)
		{
			obj = upstream.getObject(key);
			if (obj != null)
				set(obj, key);
		}
		return (T) obj;
	}

	public void set(Object object, Key key)
	{
		keyToObject.put(key, object);
		objectToKey.put(new Identity<Object>(object), key);
	}

	public void clear()
	{
		keyToObject.clear();
		objectToKey.clear();
	}

	public void remove(Key key)
	{
		objectToKey.remove(keyToObject.remove(key));
		if (upstream != null)
			upstream.remove(key);
	}
}
